#ifndef __GLF_CORE_GENERICRECURSIVEmMutexI_INCLUDED__
#define __GLF_CORE_GENERICRECURSIVEmMutexI_INCLUDED__

#include <glf/core/threadTraits.h>
#include <glf/core/lockGuard.h>
#include <glf/core/inlines.h>

namespace glf {

//! Provide recursivity for any non-recursive mutex.
/** This implementation is taken from boost. */
template<typename UnderlyingMutex>
class GenericRecursiveMutex : private NonCopyable
{
public:

	typedef LockGuard<GenericRecursiveMutex<UnderlyingMutex> >    LockScope;
	typedef UnlockGuard<GenericRecursiveMutex<UnderlyingMutex> >  UnlockScope;
	typedef TryLockGuard<GenericRecursiveMutex<UnderlyingMutex> > TryLockScope;

	//! Initializes the mutex object.
	GenericRecursiveMutex()
		: mLockingThreadId(0)
		, mRecursionCount(0)
	{
	}

	//! Releases all resources used by the mutex object.
	~GenericRecursiveMutex()
	{
	}

	//! Waits for ownership of the specified critical section object.
	/** The function returns when the calling thread is granted ownership. */
    inline bool Lock()
    {
        const ThreadId currentThreadId = GetCurrentThreadId();

        if(!TryRecursiveLock(currentThreadId))
        {
            mMutex.Lock();
			mLockingThreadId = currentThreadId; // accessed protected by the Lock
            mRecursionCount = 1;
        }

		return true;
    }

	//! Waits for ownership of the specified critical section object.
	/** The function returns when the calling thread is granted ownership, or when the
	 *  time-out interval elapses.
	 *
	 *  \return If the critical section is successfully entered, the return value is true.
	 */
    inline bool Lock(unsigned long duration)
    {
        const ThreadId currentThreadId = GetCurrentThreadId();
		return TryRecursiveLock(currentThreadId) || TryLockBasic(currentThreadId, duration);
    }

	//! Attempts to enter a critical section without blocking.
	/** If the call is successful, the calling thread takes ownership of the
	 *  critical section.
	 *
	 *  \return If the critical section is successfully entered, the return value is true.
	 */
    inline bool TryLock()
    {
        const ThreadId currentThreadId = GetCurrentThreadId();
        return TryRecursiveLock(currentThreadId) || TryLockBasic(currentThreadId);
    }

	//! Releases ownership of the specified critical section object.
	inline void Unlock()
    {
        if(!--mRecursionCount)
        {
			mLockingThreadId = 0; // accessed protected by the Lock
            mMutex.Unlock();
        }
    }

	inline UnderlyingMutex& GetUnderlyingMutex()
	{
		return mMutex;
	}

private:

    // mLockingThreadId is not volatile, nor does it uses any atomic or acquire/release
	// semantic when accessed. While it's use as a synchronization variable, it's
	// always implicitly protected by the underlying mutex.
	ThreadId        mLockingThreadId;
	unsigned long   mRecursionCount;
    UnderlyingMutex mMutex;

	//! Attempts to acquire the underlying mutex ownership without blocking.
	/** If the call is successful, the underlying mutex takes ownership of the
	 *  critical section.
	 *
	 *  \return If the underlying mutex successfully acquires ownership, the return value
	 *          is true.
	 */
    inline bool TryLockBasic(unsigned long currentThreadId)
    {
        if(mMutex.TryLock())
        {
			mLockingThreadId = (ThreadId)currentThreadId; // accessed protected by the Lock
            mRecursionCount = 1;
			return true;
        }

        return false;
    }

	//! Attempts to acquire a recursived ownership without blocking.
	/** \return If the underlying mutex already have the ownership, the return value is
	 *          true.
	 */
	inline bool TryRecursiveLock(ThreadId currentThreadId)
    {
		// This is the only place where mLockingThreadId is accessed outside
		// the Lock. The value of mLockingThreadId can be errounous / outdated
		// if we are outside the Lock. While errounous, it can never be equal
		// to currentThreadId, which is okay.
		if(mLockingThreadId == currentThreadId)
        {
            ++mRecursionCount;
			return true;
        }

        return false;
    }

	//! Waits for the underlying mutex to acquire owenship.
	/** The function returns when the underlying mutex is granted ownership, or when the
	 *  time-out interval elapses.
	 *
	 *  \return If the underlying mutex successfully acquires ownership, the return value
	 *          is true.
	 */
    inline bool TryTimedLock(ThreadId currentThreadId, unsigned long duration)
    {
        if(mMutex.Lock(duration))
		{
			mLockingThreadId = currentThreadId;  // accessed protected by the Lock
            mRecursionCount = 1;
            return true;
        }

        return false;
    }

};

} // namespace glf

#endif // __GLF_CORE_GENERICRECURSIVEmMutexI_INCLUDED__
